/*******************************************************************************
 * Copyright (c) 2004, 2005 Elias Volanakis and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *    Elias Volanakis - initial API and implementation
 *******************************************************************************/
package edu.ccut.saturn.studio.logic.parts;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.List;

import org.eclipse.draw2d.ConnectionLayer;
import org.eclipse.draw2d.Figure;
import org.eclipse.draw2d.FreeformLayer;
import org.eclipse.draw2d.FreeformLayout;
import org.eclipse.draw2d.IFigure;
import org.eclipse.draw2d.MarginBorder;
import org.eclipse.draw2d.ShortestPathConnectionRouter;
import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.gef.EditPart;
import org.eclipse.gef.EditPolicy;
import org.eclipse.gef.LayerConstants;
import org.eclipse.gef.commands.Command;
import org.eclipse.gef.editparts.AbstractGraphicalEditPart;
import org.eclipse.gef.editpolicies.RootComponentEditPolicy;
import org.eclipse.gef.editpolicies.XYLayoutEditPolicy;
import org.eclipse.gef.requests.ChangeBoundsRequest;
import org.eclipse.gef.requests.CreateRequest;

import edu.ccut.saturn.studio.logic.model.BasicShape;
import edu.ccut.saturn.studio.logic.model.Shape;
import edu.ccut.saturn.studio.logic.model.ShapesDiagram;
import edu.ccut.saturn.studio.logic.model.commands.CreateForCommand;
import edu.ccut.saturn.studio.logic.model.commands.ShapeCreateCommand;
import edu.ccut.saturn.studio.logic.model.commands.ShapeSetConstraintCommand;
import edu.ccut.saturn.studio.logic.model.saturn.SaturnEndIterationNode;
import edu.ccut.saturn.studio.logic.model.saturn.SaturnStartIterationNode;
import edu.ccut.saturn.studio.logic.policy.ResizaEdidPolicyNoHandle;

/**
 * EditPart for the a ShapesDiagram instance.
 * <p>
 * This edit part server as the main diagram container, the white area where
 * everything else is in. Also responsible for the container's layout (the way
 * the container rearanges is contents) and the container's capabilities (edit
 * policies).
 * </p>
 * <p>
 * This edit part must implement the PropertyChangeListener interface, so it can
 * be notified of property changes in the corresponding model element.
 * </p>
 * 
 * @author Elias Volanakis
 */
public class DiagramEditPart extends AbstractGraphicalEditPart implements
		PropertyChangeListener {

	public void activate() {
		if (!isActive()) {
			super.activate();
			((BasicShape) getModel()).addPropertyChangeListener(this);
		}
	}

	protected void createEditPolicies() {
		installEditPolicy(EditPolicy.COMPONENT_ROLE,
				new RootComponentEditPolicy());
		installEditPolicy(EditPolicy.LAYOUT_ROLE,
				new ShapesXYLayoutEditPolicy());
	}

	protected IFigure createFigure() {
		Figure f = new FreeformLayer();
		f.setBorder(new MarginBorder(3));
		f.setLayoutManager(new FreeformLayout());

		ConnectionLayer connLayer = (ConnectionLayer) getLayer(LayerConstants.CONNECTION_LAYER);
		connLayer.setConnectionRouter(new ShortestPathConnectionRouter(f));

		return f;
	}

	public void deactivate() {
		if (isActive()) {
			super.deactivate();
			((BasicShape) getModel()).removePropertyChangeListener(this);
		}
	}

	private ShapesDiagram getCastedModel() {
		return (ShapesDiagram) getModel();
	}

	protected List<Shape> getModelChildren() {
		return getCastedModel().getShapes(); // return a list of shapes
	}

	public void propertyChange(PropertyChangeEvent evt) {
		String prop = evt.getPropertyName();
		if (ShapesDiagram.CHILD_ADDED_PROP.equals(prop)
				|| ShapesDiagram.CHILD_REMOVED_PROP.equals(prop)) {
			refreshChildren();
		}
	}

	/**
	 * EditPolicy for the Figure used by this edit part. Children of
	 * XYLayoutEditPolicy can be used in Figures with XYLayout.
	 * 
	 * @author Elias Volanakis
	 */
	private static class ShapesXYLayoutEditPolicy extends XYLayoutEditPolicy {

		public ShapesXYLayoutEditPolicy() {

		}

		protected Command createChangeConstraintCommand(
				ChangeBoundsRequest request, EditPart child, Object constraint) {
			if (child instanceof ShapeEditPart
					&& constraint instanceof Rectangle) {
				// return a command that can move and/or resize a Shape
				return new ShapeSetConstraintCommand((Shape) child.getModel(),
						request, (Rectangle) constraint);
			}
			return super.createChangeConstraintCommand(request, child,
					constraint);
		}

		protected Command createChangeConstraintCommand(EditPart child,
				Object constraint) {
			return null;
		}

		@SuppressWarnings("unchecked")
		protected Command getCreateCommand(CreateRequest request) {
			Object childClass = request.getNewObjectType();

			if (this.isSubclass(SaturnStartIterationNode.class,
					(Class) childClass)) {
				SaturnStartIterationNode saturnStartIterationNode = (SaturnStartIterationNode) request
						.getNewObject();
				ShapesDiagram diagram = (ShapesDiagram) getHost().getModel();

				return new CreateForCommand(saturnStartIterationNode,
						new SaturnEndIterationNode(), diagram,
						(Rectangle) getConstraintFor(request));
			}
			if (this.isSubclass(Shape.class, (Class) childClass)) {
				Shape shape = (Shape) request.getNewObject();
				ShapesDiagram diagram = (ShapesDiagram) getHost().getModel();
				shape.setId(diagram.getNextValidShapeId());

				return new ShapeCreateCommand(shape, diagram,
						(Rectangle) getConstraintFor(request));
			}

			return null;
		}

		@SuppressWarnings("unchecked")
		private boolean isSubclass(Class baseClass, Class subClass) {
			Class<?> subBase = subClass;

			do {
				subBase = subBase.getSuperclass();

				if (subBase == baseClass) {
					return true;
				}
			} while (subBase != null);

			return false;
		}

		// 用于改变handles的颜色.控制handles的图形大小及其图像。
		protected EditPolicy createChildEditPolicy(EditPart child) {
			return new ResizaEdidPolicyNoHandle();
		}
	}

}